
public class HolePenaltyFeature implements Feature {
	final static int ROWS = 20;
	final static int COLS = 10;
	@Override
	
	public double computeScore(Stateview s) {
		int[][] field = s.getField();
		int[]top = s.getTop();
		int score = 0;
		
		//Heuristics
		score -= computeHoleSizeWeight(field, top)* 10;
		score -= computeEmptySpace(field, top) * 20;
		score -= computeHolesPerRow(field, top) * 10;
		
		
		return score;
	}
	
	//Start Heuristics 
	
	//More unconnected holes per row = bad.
	//Penalty based
	private int computeHolesPerRow(int[][] field, int[] top)
	{
		int minHeight = 100;
		int weight = 0;
		
		//Only compute rows which are fully filled. 
		for(int i = 0; i < top.length; i++)
		{
			if(minHeight > top[i]) minHeight = top[i];
		}
		
		for(int row = 0; row < minHeight; row++)
		{
			int unconnectedHoles = 0;
			for(int col = 0; col < COLS; col++)
			{
				if(field[row][col] == 0)
				{
					unconnectedHoles++;
					
					while(++col < COLS && field[row][col] == 0);
				}
			}
			
			/* Comment/ Uncomment
			
			//Higher rows have higher penalty
			unconnectedHoles *= row; 
			
			//Quadratic increase
			unconnectedHoles *= unconnectedHoles;
			
			*/
			
			weight += unconnectedHoles;
		}
		return weight;
	}
	
	//Search for how deep the highest hole is buried
	//Penalty based
	private int computeHoleBurial(int[][] field, int[] top)
	{
		int weight = 0;
	    for(int col = 0; col < COLS; col++)
	    {
	    	int depth = 0;
	    	while(depth <= top[col] && field[top[col] - depth][col] != 0) depth++;
	    	if(depth <= top[col]) //got hole
	    	{
	    		int heightOfHole = top[col] - depth + 1;
	    		//heightOfHole *= heightOfHole; //the higher the hole, the more severe the weightage
	    		weight += heightOfHole * depth;
	    	}
	    }
	    return weight;
	}
	
	//Calculate the how big holes' sizes are. Used by multiplying holes' sizes (Many big holes = BAD)
	//Try to prevent computer from covering up big holes
	//Penalty based
	private int computeHoleSizeWeight(int[][] field, int[] top)
	{
		int holeSize = 0;
		boolean calculated[][] = new boolean[ROWS][COLS];
		for(int i = 0; i < COLS; i++)
			for(int j = 0; j < ROWS; j++) 
				calculated[j][i] = false;

		for(int col = 0; col < COLS; col++)
		{
			for(int row = 0; row < ROWS; row++)
			{
				if(field[row][col] == 0 && !calculated[row][col])
				{
					if(holeSize == 0) holeSize = 1;
					holeSize *= computeHoleSize(field, calculated, col, row);
				}
			}
		}
		return holeSize;
	}

	//Count the number of empty spaces 
	//Penalty Based
	private int computeEmptySpace(int[][] field, int[] top)
	{
		int totalEmpty = 0;
		for(int i = 0; i < COLS; i++)
		{
			int empty = 0;
			for(int j = 0; j < top[i] + 1; j++)
			{
				if(field[j][i] > 0)
				{
					totalEmpty += empty;
					empty = 0;
				}
				else if(field[j][i] == 0) 
				{
					//Linear increase
					//empty++;
					
					//Increase based on row number(higher row, higher penalty)
					empty += j;
				}
			}
		} 
		return totalEmpty;
	}

	//End Heuristics
	// avoid recursive if possible
	// not really efficient
	private int computeHoleSize(int[][] field, boolean [][] calculated, int colNo, int rowNo)
	{
		if(calculated[rowNo][colNo]) 
		{
			return 0;
		}
		int holeSize = 1;
		calculated[rowNo][colNo] = true;
		for(int i = colNo; i < colNo+1; i++)
		{
			if(i < COLS)
			{
				for(int j = rowNo; j < rowNo + 1; j++)
				{
					if(j < ROWS && field[j][i] == 0)
					{
						holeSize += computeHoleSize(field, calculated, i, j);
					}
				}
			}
		}
		return holeSize;                                  
	}
	
	/*
	private int computeHoleSize(int[][] field, int[] top) {
		int holeSize = 0;
		
		// compute the size of holes
		for(int col=0; col<Stateview.COLS; col++) {
			for(int row=0; row<top[col]; row++) {
				if(field[row][col] == 0)
					holeSize++;
			}
		}
			
		return holeSize;
	}*/
}
